#include <arpa/inet.h>
#include <cstring>
#include <cstdlib>
#include <string>

#include "../../tools/Log.h"
#include "../../tools/MemDump.h"
#include "../commands/GameCommand.h"
#include "GamePacket.h"

//#define DEBUG_NETWORK

using namespace std;

namespace GamePacket
{

Header::Header(void *packet) : type(0), packet_len(0), seq_num(0)
{	
	*this = *((Header *) (packet));
	ntoh();

#ifdef DEBUG_NETWORK
	lout << "Received: ";
	print();
#endif
}

void Header::hton()
{
	type = htons(type);
	seq_num = htonl(seq_num);
	packet_len = htons(packet_len);
}

void Header::ntoh()
{
	type = ntohs(type);
	seq_num = ntohl(seq_num);
	packet_len = ntohs(packet_len);
}

void Header::print()
{
	string type_name;
	switch(type)
	{
		case ACK:
			type_name = "ACK";
			break;
		case UPDATE:
			type_name = "UPDATE";
			break;
		default: break;
	}
	
	lout << type_name << " (" << seq_num << ")" << endl;
}

Ack::Ack(void *packet):data()
{
	memcpy(&data, packet, sizeof(Ack::Data));
	ntoh();
	
#ifdef DEBUG_NETWORK
	lout << "Received: ";
	data.header.print();
#endif
}

Ack::Ack(unsigned long seq_num):data() {
	data.header = Header(ACK, sizeof(Ack::Data), seq_num);
}

void Ack::hton()
{
	data.header.hton();
}

void Ack::ntoh()
{
	data.header.ntoh();
}

void Ack::convert_and_send(int sock, struct sockaddr_in *to)
{
	Ack pkt_copy = *this;
	unsigned short packet_len = data.header.packet_len;		// Destroyd in conversion

#ifdef DEBUG_NETWORK
	lout << "Sending:  ";
	pkt_copy.data.header.print();
#endif
	
	pkt_copy.hton();	
	sendto(sock, &pkt_copy.data, packet_len, 0, (struct sockaddr *) to, (socklen_t) sizeof(struct sockaddr));
}

Update::Update(void *packet): data()
{
	memcpy(&data, packet, sizeof(Update::Data));
	ntoh();
}

Update::Update(unsigned long ok_time, unsigned int seq_num): data()
{
	data.ok_time = ok_time;
	data.num_commands = 0;
	data.header = Header(UPDATE, get_current_size(), seq_num);
}

void Update::add_command(GameCommand &cmd)
{
	data.commands[data.num_commands] = cmd.serialize();
	data.num_commands ++;
	data.header.packet_len = get_current_size();
}

int Update::get_current_size()
{
	return sizeof(Update) - (MAX_COMMANDS - data.num_commands) * sizeof(SerializedGameCommand);
}

void Update::hton()
{
	data.header.hton();
	data.ok_time = htonl(data.ok_time);
	data.num_commands = htonl(data.num_commands);
}

void Update::ntoh()
{
	data.header.ntoh();
	data.ok_time = ntohl(data.ok_time);
	data.num_commands = ntohl(data.num_commands);
}

void Update::convert_and_send(int sock, struct sockaddr_in *to)
{
	Update pkt_copy = *this;
	unsigned short packet_len = data.header.packet_len;		// Destroyd in conversion
	
#ifdef DEBUG_NETWORK
	lout << "Sending:  ";
	pkt_copy.data.header.print();
#endif
	
	pkt_copy.hton();
	sendto(sock, &(pkt_copy.data), packet_len, 0, (struct sockaddr *) to, (socklen_t) sizeof(struct sockaddr));
}

}
